//
// Created by 郝羽 on 2017/12/12.
//

#ifndef RTC_XSWITCH2_INTER_ARRIVAL_HPP
#define RTC_XSWITCH2_INTER_ARRIVAL_HPP

#include <iostream>

namespace bwe {

class PacketGroup {
public:
    bool IsFirstPacket() {
        return size == 0;
    };

public:
    int32_t size = 0;
    uint32_t first_ts = 0;
    uint32_t last_ts = 0;
    int64_t complete_ms = -1;
    int64_t last_system_time_ms = -1;
};

class InterArrival {
public:
    InterArrival(uint32_t group_length_ticks, double timestamp_to_ms)
            : group_length_ticks_(group_length_ticks),
              timestamp_to_ms_(timestamp_to_ms)
    {};

    bool ComputeDelta(uint32_t &ts_delta, int64_t &t_delta_ms, int32_t &size_delta, uint32_t rtp_ts, int64_t arrival, int32_t size, int64_t now_ms) {
        bool has_delta = false;
        if (cur_group_.IsFirstPacket()) {
            cur_group_.first_ts = rtp_ts;
            cur_group_.last_ts = rtp_ts;
        } else if (!inOrder(rtp_ts)) {
            //std::cout << "re-order first" << std::endl;
            return false;
//        } else if (!inOrderLast(rtp_ts)) {
//            //std::cout << "re-order last" << std::endl;
//            return false;
        } else if (!inCurrentGroup(rtp_ts, arrival)) {
            if (prev_group_.complete_ms >= 0) {

                ts_delta = cur_group_.last_ts - prev_group_.last_ts;
                t_delta_ms = cur_group_.complete_ms - prev_group_.complete_ms;
                int64_t system_time_delta_ms = cur_group_.last_system_time_ms - prev_group_.last_system_time_ms;
                if (t_delta_ms - system_time_delta_ms >= arrival_time_offset_threshold_) {
                    // std::cout << "The arrival time clock offset has changed (diff = "
                    //           << t_delta_ms - system_time_delta_ms
                    //           << " ms), resetting.";
                    Reset();
                    return false;
                }
                if (t_delta_ms < 0) {
                    consecutive_reordered_ += 1;
                    if (consecutive_reordered_ > reorder_max_) {
                        Reset();
                    }
                    return false;
                } else {
                    consecutive_reordered_ = 0;
                }
                size_delta = cur_group_.size - prev_group_.size;
                has_delta = true;
                // std::cout << "inter-arrival: (cur/pre) last-ts(" << cur_group_.last_ts << "/" << prev_group_.last_ts << ")";
                // std::cout << " complete_time_ms: (" << cur_group_.complete_ms << "/" << prev_group_.complete_ms << ")";
                // std::cout << " size: (" << cur_group_.size << "/" << prev_group_.size << ")" << std::endl;
            }
            prev_group_ = cur_group_;
            cur_group_.first_ts = rtp_ts;
            cur_group_.last_ts = rtp_ts;
            cur_group_.size = 0;
        } else {
            cur_group_.last_ts = lastestTimestamp(rtp_ts, cur_group_.last_ts);
        }
        cur_group_.size += size;
        cur_group_.complete_ms = arrival;
        cur_group_.last_system_time_ms = now_ms;
        return has_delta;
    };


    bool inOrder(uint32_t ts) {
        uint32_t d = ts - cur_group_.first_ts;
        return d < 0x80000000;
    };

    bool inOrderLast(uint32_t ts) {
        uint32_t d = ts - cur_group_.last_ts;
        return d < 0x80000000;
    };

    uint32_t lastestTimestamp(uint32_t ts1, uint32_t ts2) {
        uint32_t d = ts1 - ts2;
        return (d < 0x80000000 ? ts1 : ts2);
    };

    bool inCurrentGroup(uint32_t ts, int64_t arrival) {
        int64_t t_diff_ms = arrival - cur_group_.complete_ms;
        uint32_t ts_diff = ts - cur_group_.last_ts;
        if (ts_diff == 0) {
//            std::cout << "ts(" << ts << ") - cg.last_ts(" << cur_group_.last_ts << ") == 0" << std::endl;
            return true;
        }
//        if (cur_group_.last_ts - ts <= group_length_ticks_) {
////            std::cout << "cg.last_ts(" << cur_group_.last_ts << ") - ts(" << ts << ") <= " << group_length_ticks_ << std::endl;
//            return true;
//        }
        int64_t ts_diff_ms = ts_diff * timestamp_to_ms_ + 0.5;
        int64_t t_ts_diff_ms = t_diff_ms - ts_diff_ms;
        if (t_ts_diff_ms < 0 && t_diff_ms <= burst_ms_) {
//            std::cout << "t_ts_diff (" << t_ts_diff_ms << ") < 0 && t_diff_ms(" << t_diff_ms << ") < burst" << std::endl;
            return true;
        }

        uint32_t ts_first_diff = ts - cur_group_.first_ts;
//        std::cout << "ts(" << ts << ") - cg.first_ts(" << cur_group_.first_ts << ") = " << ts_first_diff << std::endl;
        return ts_first_diff <= group_length_ticks_;
    };

    void Reset() {
        consecutive_reordered_ = 0;
        cur_group_ = PacketGroup();
        prev_group_ = PacketGroup();
    };

private:
    const uint32_t group_length_ticks_;
    const double   timestamp_to_ms_;
    const int      burst_ms_            = 5;
    const int      burst_ts_            = burst_ms_ * 90;
    const int      reorder_max_         = 3;
    const int64_t  arrival_time_offset_threshold_ = 3000;

    PacketGroup    cur_group_;
    PacketGroup    prev_group_;
    int            consecutive_reordered_ = 0;


};

}
#endif //RTC_XSWITCH2_INTER_ARRIVAL_HPP
